package util;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.ServletContext;

import org.springframework.beans.factory.annotation.Autowired;

import entity.Configure;
import entity.Video;
import entity.VideoState;
import service.ConfigureService;
import service.VideoService;
import service.VideoStateService;

/**
 * 视频转码线程
 */
public class VideoTranscoderThread extends Thread {

	// 使用：@Component、@Controller、@Service、@Repository注解， 扫描的组件默认别名为”类名首字母小写”。
	ConfigureService configureService = (ConfigureService) SpringContextUtils.getSpringBean("configureServiceImpl"); // 此处为什么小写
	VideoService videoService = (VideoService) SpringContextUtils.getSpringBean("videoServiceImpl");
	VideoStateService videoStateService = (VideoStateService) SpringContextUtils.getSpringBean("videoStateServiceImpl");

	private ServletContext servletContext;

	@Override
	public void run() {
		try {    
			
			int order = 3; // 表示id为3的视频状态

			// 获取转码需要的各种配置参数
			Configure transcoder_vcodec_cfg = configureService.findSingle("transcoder_vcodec");
			Configure transcoder_bv_cfg = configureService.findSingle("transcoder_bv");
			Configure transcoder_framerate_cfg = configureService.findSingle("transcoder_framerate");
			Configure transcoder_acodec_cfg = configureService.findSingle("transcoder_acodec");
			Configure transcoder_ar_cfg = configureService.findSingle("transcoder_ar");
			Configure transcoder_ba_cfg = configureService.findSingle("transcoder_ba");
			Configure transcoder_scale_w_cfg = configureService.findSingle("transcoder_scale_w");
			Configure transcoder_scale_h_cfg = configureService.findSingle("transcoder_scale_h");
			Configure transcoder_watermarkuse_cfg = configureService.findSingle("transcoder_watermarkuse");
			Configure transcoder_watermark_url_cfg = configureService.findSingle("transcoder_watermark_url");
			Configure transcoder_watermark_x_cfg = configureService.findSingle("transcoder_watermark_x");
			Configure transcoder_watermark_y_cfg = configureService.findSingle("transcoder_watermark_y");
			Configure transcoder_keepaspectratio_cfg = configureService.findSingle("transcoder_keepaspectratio");
			Configure transcoder_outfmt_cfg = configureService.findSingle("transcoder_outfmt");
			Configure folder_video_cfg = configureService.findSingle("folder_video");

			// 设置视频的水印效果
			String[] waterMarkStrList = transcoder_watermark_url_cfg.getVal().split("/");
			String waterMarkDir = ""; // 保存路径，不包括文件名
			
			for (int i = 0; i < waterMarkStrList.length - 1; i++) {
				waterMarkDir += waterMarkStrList[i] + "/";
			}

			String realWaterMarkDir = servletContext.getRealPath("/").replace('\\', '/') + waterMarkDir;
			File realWaterMarkDirFile = new File(realWaterMarkDir);
			// 为了确保文件夹存在，否者创建，文件夹不存在可能造成文件读取异常;
			if (!realWaterMarkDirFile.exists() && !realWaterMarkDirFile.isDirectory()) {
				realWaterMarkDirFile.mkdirs();
			}
			System.out.println(realWaterMarkDirFile);
			
			// 转码后文件路径处理
			String realFileDir = servletContext.getRealPath("/").replace('\\', '/') + folder_video_cfg.getVal(); // 获取存放转码后文件的完整路径
			File realFileDirFile = new File(realFileDir);
			// 为了确保文件夹存在，否者创建，文件夹不存在可能造成文件读取异常;
			if (!realFileDirFile.exists() && !realFileDirFile.isDirectory()) { // 检查是否存在此路径，否则创建
				realFileDirFile.mkdirs();
			}

			while (true) {
				// 获取数据库中，等待转码的视频集合，由于是在服务器方进行转码，不需要客户的信息，全部的信息进行截取
				List<Video> videoList = videoService.findByState(order);
				if (videoList != null) {
					VideoState finished = videoStateService.findById(order+1);
					for (Video video : videoList) {
						//输出文件的存入路径和文件名
						String filePath = folder_video_cfg.getVal() + "/" + video.getId() + "." + transcoder_outfmt_cfg.getVal();
						video.setUrl(filePath);
						String realFilePath = servletContext.getRealPath("/").replace('\\', '/') + video.getUrl();
						
						//视频文件的原视频路径信息
						String realFileOriginalPath = servletContext.getRealPath("/").replace('\\', '/') + video.getOriUrl();
						
						//转码命令如下所示
						//ffmpeg -i xxx.mkv -ar 22050 -b 600k -vcodec libx264 
						//-vf scale=w=640:h=360:force_original_aspect_ratio=decrease,pad=w=640:h=360:x=(ow-iw)/2:y=(oh-ih)/2[aa];
						//movie=watermark.png[bb];[aa][bb]overlay=5:5 yyy.flv
						//AVFilter参数作用如下所示
						//scale:视频拉伸滤镜。force_original_aspect_ratio用于强制保持宽高比
						//pad:用于加黑边，四个参数含义分别为：处理后宽，处理后高，输入图像左上角x坐标，输入视频左上角Y坐标。
						//其中ow,oh为输出（填充后）视频的宽高；iw,ih为输入（填充前）视频的宽高。
						//movie：用于指定需要叠加的水印Logo（PNG文件）。
						//overlay:用于叠加水印Logo和视频文件
						//命令行不同的执行方式
						//cmd /c xxx 是执行完xxx命令后关闭命令窗口。
						//cmd /k xxx 是执行完xxx命令后不关闭命令窗口。
						
						
						//参数含义如下：
						//-y：输出文件重名的时候，自动覆盖。
						//-i：输入文件路径（可以是相对路径或者绝对路径）。
						//-vcodec：视频编码器，这里是libx264。
						//-b:v：视频码率，这里是500000bps。
						//-r：视频帧率，这里是25fps。
						//-acodec：音频编码器，这里是libmp3lame。
						//-b:a：音频码率，这里是64000bps。
						//-ar：音频采样率，这里是22050Hz。
						//-vf：滤镜，用于图像拉伸以及水印叠加。
						//最后一个参数为输出的视频文件路径。
						
						//转码命令
						String videoTranscodeCommand = "cmd ";
						videoTranscodeCommand += "/c start ";
						videoTranscodeCommand += "ffmpeg -y ";
						videoTranscodeCommand += "-i ";
						videoTranscodeCommand += "\"" + realFileOriginalPath + "\" ";
						videoTranscodeCommand += "-vcodec " + transcoder_vcodec_cfg.getVal() + " ";
						videoTranscodeCommand += "-b:v "+transcoder_bv_cfg.getVal()+" ";
						videoTranscodeCommand += "-r "+transcoder_framerate_cfg.getVal()+" ";
						videoTranscodeCommand += "-acodec "+transcoder_acodec_cfg.getVal()+" ";
						videoTranscodeCommand += "-b:a "+transcoder_ba_cfg.getVal()+" ";
						videoTranscodeCommand += "-ar "+transcoder_ar_cfg.getVal()+" ";
						videoTranscodeCommand += "-vf ";
						videoTranscodeCommand += "scale=w="+transcoder_scale_w_cfg.getVal()+":h="+transcoder_scale_h_cfg.getVal();
						
						String waterMarkFile = video.getUser().getNickname() + ".png"; // 获取水印文件的文件名
						
						//如果保持宽高比并且填充黑边
						if(transcoder_keepaspectratio_cfg.getVal().equals("true")) {
							videoTranscodeCommand += ":" +
									"force_original_aspect_ratio=decrease,pad=w=" +
									transcoder_scale_w_cfg.getVal() + ":h=" +
									transcoder_scale_h_cfg.getVal() + ":x=(ow-iw)/2:y=(oh-ih)/2";
						}
						videoTranscodeCommand += "[aa]";
						//如果开始视频水印
						if(transcoder_watermarkuse_cfg.getVal().equals("true")) {
							videoTranscodeCommand += ";movie=";
							videoTranscodeCommand += waterMarkFile;
							videoTranscodeCommand += "[bb];";
							videoTranscodeCommand += "[aa][bb]";
							videoTranscodeCommand += "overlay=x=" + transcoder_watermark_x_cfg.getVal()
							+ ":y=" + transcoder_watermark_y_cfg.getVal() + " ";
						} else {
							videoTranscodeCommand += " ";
						}
						videoTranscodeCommand += "\"";
						videoTranscodeCommand += realFilePath;
						videoTranscodeCommand += "\"";
						
						System.out.println(videoTranscodeCommand);
						
						//开始执行转码命令    --- 需要两个文件，一个是需要转码的视频在命令中获取，二是水印文件，水印文件需要提供路径文件
						Process process = Runtime.getRuntime().exec(videoTranscodeCommand, null, realWaterMarkDirFile);
						
						//视频输出
						//获取二进制字节流
						BufferedInputStream in = new BufferedInputStream(process.getInputStream());
						BufferedInputStream err = new BufferedInputStream(process.getErrorStream());
						//输入流适配器
						BufferedReader inBr = new BufferedReader(new InputStreamReader(in));
						BufferedReader errBr = new BufferedReader(new InputStreamReader(err));
						String lineStr;
						//循环读取流
						while((lineStr = inBr.readLine()) != null) { //如果没有读取完毕
							System.out.println(lineStr);  //输出
						}
						while((lineStr = errBr.readLine()) != null) { //如果没有读取完毕
							System.out.println(lineStr);  //输出
						}
						
						if(process.waitFor() != 0 ) {
							if(process.exitValue() == 1) { //process.exitValue()==0表示正常结束，1：非正常结束  
								System.err.println("视频转码出错!");  
							}
						}
						errBr.close();
						inBr.close();
						err.close();
						in.close();
						
						int videoTime = getVideoTime(realFilePath, "D:\\ffmpeg-20180227-fa0c9d6-win64-static\\bin\\ffmpeg.exe");
						SimpleDateFormat sdf = new SimpleDateFormat("ss");
						Date parse = sdf.parse(videoTime+"");
						
						video.setVideoState(finished);
						video.setDuration(parse);
						videoService.updateVideoState(video);
						videoService.updateVideoUrl(video);
						sleep(60 * 1000);
					}
				}
				sleep(60 * 1000);
			}
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
	}

	public ServletContext getServletContext() {
		return servletContext;
	}

	public void setServletContext(ServletContext servletContext) {
		this.servletContext = servletContext;
	}

	public VideoTranscoderThread(ServletContext servletContext) {
		super();
		this.servletContext = servletContext;
	}

	public VideoTranscoderThread() {
		super();
	}
	
	//获取视频长度
	private int getVideoTime(String video_path, String ffmpeg_path) {  
        List<String> commands = new java.util.ArrayList<String>();  
        commands.add(ffmpeg_path);  
        commands.add("-i");  
        commands.add(video_path);  
        try {  
            ProcessBuilder builder = new ProcessBuilder();  
            builder.command(commands);  
            final Process p = builder.start();  
              
            //从输入流中读取视频信息  
            BufferedReader br = new BufferedReader(new InputStreamReader(p.getErrorStream()));  
            StringBuffer sb = new StringBuffer();  
            String line = "";  
            while ((line = br.readLine()) != null) {  
                sb.append(line);  
            }  
            br.close();  
              
            //从视频信息中解析时长  
            String regexDuration = "Duration: (.*?), start: (.*?), bitrate: (\\d*) kb\\/s";  
            Pattern pattern = Pattern.compile(regexDuration);  
            Matcher m = pattern.matcher(sb.toString());  
            if (m.find()) {  
            	System.out.println(m.group(1));
                int time = getTimelen(m.group(1));  
                return time;  
            }  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
          
        return 0;  
    }  
	
	//调整时间内容
	private int getTimelen(String timelen){  
        int min=0;  
        String strs[] = timelen.split(":");  
        if (strs[0].compareTo("0") > 0) {  
            min+=Integer.valueOf(strs[0])*60*60;//秒  
        }  
        if(strs[1].compareTo("0")>0){  
            min+=Integer.valueOf(strs[1])*60;  
        }  
        if(strs[2].compareTo("0")>0){  
            min+=Math.round(Float.valueOf(strs[2]));  
        }  
        return min;  
    }
	
}
